package com.br4mmie.asciistory.map;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.*;

import com.br4mmie.asciistory.*;
import com.br4mmie.asciistory.battle.BattleWindow;
import com.br4mmie.asciistory.battle.EnemyType;
import com.br4mmie.asciistory.mission.Item;


/**
 * The map class manages a map.
 * @author 	Bram Buurlage
 * 			Ken Veenstra
 */
public class Map implements Restorable
{  
	private CharImage image;
	private boolean[][] solid;
	private int width;
	private int height;
	
	private int playerX;
	private int playerY;
	
	private ArrayList<MapObject> mapObjects;
	private ArrayList<Encounter> encounters;
	
	private Random random = new Random();
	private String origFileName;
	
	/**
	 * Creates a new map. DUH!!
	 * you should not use this constructor, use MapLoader.createMap instead.
	 * @param width the width for the map 
	 * @param height the height for the map
	 * @param map the data that this map should display
	 * @param solid the collision data for this map
	 * @param playerX the location where the player should begin
	 * @param playerY see playerX
	 */
	public Map(int width, int height,String map, boolean[][] solid, int playerX, int playerY)
	{
		mapObjects = new ArrayList<MapObject>();
		encounters = new ArrayList<Encounter>();
		image = new CharImage(width,height);
		image.writeImage(map,0,0);
		
		this.width = width;
		this.height = height;
		this.solid = solid;
		
		this.playerX = playerX;
		this.playerY = playerY;
	}
	
	public String getFilename()
	{
		String[] segs = origFileName.split("/"); 
		return segs[segs.length-1];
	}
	
	/**
	 * draws the map objects to a CharImage
	 * @param target the CharImage that the map will be drawn on
	 */
	public void draw(CharImage target, Camera camera, int screenW, int screenH)
	{
		image.draw(target, 0, 0, camera.getX(), camera.getY(), screenW, screenH);
		for(MapObject m : mapObjects)
		{
			if (m.getX() > camera.getX() && 
				m.getX() < screenW+camera.getX() &&
				m.getY() > camera.getY() &&
				m.getY() < screenH+camera.getY())
			{
				m.draw(target, camera.getX(), camera.getY());
			}
		}
	}
	public CharImage getImage()
	{
		return image;
	}
	
	public void setFilename(String fileName)
	{
		origFileName = fileName;
	}
	/**
	 * returns wether a position on the map is solid
	 * @param x the location to check
	 * @param y see x
	 * @return wether (x,y) is solid
	 */
	public boolean getCollision(int x, int y)
	{
		return solid[y][x];
	}
	public int getWidth() 
	{
		return width;
	}
	public int getHeight()
	{
		return height;
	}
	
	/**
	 * These methods add and remove MapObjects from the map.
	 * Added MapObjects will be drawn when Map.draw() is called
	 */
	public void addItem(Item item, int x, int y)
	{
		//items should be added below any other objects..
		addMapObjectBelow(item.createMapObject(x, y));
		//addMapObject(item.createMapObject(x, y));
	}
	public void addMapObject(MapObject obj)
	{
		mapObjects.add(obj);
	}
	public void addMapObjectBelow(MapObject obj)
	{
		mapObjects.add(0, obj);
	}
	public void removeMapObject(MapObject obj)
	{
		mapObjects.remove(obj);
	}
	public void addEncounter(Encounter e)
	{
		encounters.add(e);
	}
	/**
	 * This method returns a list containg all MapObjects on Location (x,y)
	 */
	public List<MapObject> getObjectsAt(int x, int y)
	{
		ArrayList<MapObject> objects = new ArrayList<MapObject>();
		
		for (MapObject mo: mapObjects)
		{
			if (mo.getX() == x &&
				mo.getY() == y)
			{
				objects.add(mo);
			}
		}
		
		return objects;
	}
	@SuppressWarnings("unchecked")
	public List<MapObject> getAllObjects()
	{
		return (List<MapObject>) mapObjects.clone();
	}
	public int getMapObjectAt( int x, int y)
	{
		ArrayList<MapObject> objects = new ArrayList<MapObject>();
		
		for (MapObject mo: mapObjects)
		{
			if (mo.getX() == x &&
				mo.getY() == y)
			{
				objects.add(mo);
			}
		}
		
		return objects.size();
	}
	public void encounter()
	{
		ArrayList<EnemyType> ets = new ArrayList<EnemyType>();
		for (int i=0; i<2; i++)
		{
			if (encounters.size() > 0)
			{
				Encounter e = encounters.get(random.nextInt(encounters.size()));
				String enemy = e.encounter();
				if (enemy != null)
				{
					ets.add(new EnemyType(enemy));
				}
			}
		}
		
		if (ets.size() > 0)
		{
			CharImage background = new CharImage(ASCIIStoryActivity.getScreenWidth()-1, ASCIIStoryActivity.getScreenHeight()-1);
			background.fill(' ');
			
			EnemyType[] et = new EnemyType[ets.size()];
			for (int i=0; i<ets.size(); i++) et[i] = ets.get(i);
			
			new BattleWindow(et, background, ASCIIStoryActivity.getGameLoop().randomEncounter);
		}
	}
	
	/**
	 * these getters return the position where the player should begin.
	 */
	public int getSpawnX() { return playerX; }
	public int getSpawnY() { return playerY; }

	public static Map restore(BufferedReader reader)
	{
		try 
		{
			MapLoader mapLoader = new MapLoader(reader.readLine());
			reader.readLine();
			
			return mapLoader.createMap();
		} catch (NumberFormatException e) 
		{
			e.printStackTrace();
		} catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		return null;
	}
	public void save(BufferedWriter writer) 
	{
		try
		{
			writer.write("map\n");
			writer.write(origFileName+"\n");
			writer.write("end\n");
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
}